﻿namespace GenericListExercise
{
    using System;
    using System.Text;

    public class GenericList<T> where T : IComparable
    {
        //// Write a generic class GenericList<T> that keeps a list of elements of some parametric type T.
        //// Keep the elements of the list in an array with fixed capacity which is given as parameter in the class constructor.
        //// Implement methods for adding element, accessing element by index, removing element by index, inserting element at
        //// given position, clearing the list, finding element by its value and ToString(). Check all input parameters to
        //// avoid accessing elements at invalid positions. Implement auto-grow functionality: when the internal array is full,
        //// create a new array of double size and move all elements to it.
        //// Create generic methods Min<T>() and Max<T>() for finding the minimal and maximal element in the  GenericList<T>.
        //// You may need to add a generic constraints for the type T.

        private const int DefaultCapacity = 16;

        private T[] coreData;
        private int indexer;

        public GenericList()
        {
            this.coreData = new T[DefaultCapacity];
            this.indexer = 0;
        }

        public GenericList(int capacity) : this()
        {
            this.coreData = new T[capacity];
        }

        private int Indexer
        {
            get
            {
                return this.indexer;
            }

            set
            {
                this.indexer = value;
            }
        }

        public T this[int index]
        {
            get
            {
                if (index >= this.Indexer || index < 0)
                {
                    throw new ArgumentOutOfRangeException("Be careful with the index.");
                }

                return this.coreData[index];
            }

            set
            {
                if (index >= this.Indexer || index < 0)
                {
                    throw new ArgumentOutOfRangeException("Be careful with the index.");
                }

                this.Add(value, index);
            }
        }

        public override string ToString()
        {
            StringBuilder result = new StringBuilder();

            for (int i = 0; i < this.Indexer; i++)
            {
                result.Append(this.coreData[i].ToString());
                result.Append(Environment.NewLine);
            }

            result.Length--;

            return result.ToString();
        }

        public void Add(T element)
        {
            this.coreData[this.Indexer] = element;

            this.Indexer++;

            if (this.OverflowCheck() == true)
            {
                this.Resize();
            }
        }

        public void Add(T element, int index)
        {
            this.Indexer++;

            if (this.OverflowCheck() == true)
            {
                this.Resize();
            }

            T tempFirst = this.coreData[index];
            this.coreData[index] = element;

            for (int i = index + 1; i < this.Indexer; i++)
            {
                T tempSecond = this.coreData[i];
                this.coreData[i] = tempFirst;
                tempFirst = tempSecond;
            }
        }

        public void RemoveAt(int index)
        {
            for (int i = index; i < this.Indexer; i++)
            {
                this.coreData[i] = this.coreData[i + 1];
            }

            this.Indexer--;
        }

        public void Clear()
        {
            this.coreData = new T[16];
            this.Indexer = 0;
        }

        public int Find(T element)
        {
            int index = -1;

            for (int i = 0; i < this.Indexer; i++)
            {
                if (this.coreData[i].Equals(element))
                {
                    index = i;
                    break;
                }
            }

            return index;
        }

        public T Max()
        {
            T result = this.coreData[0];

            for (int i = 1; i < this.Indexer; i++)
            {
                if (this.coreData[i].CompareTo(result) > 0)
                {
                    result = this.coreData[i];   
                }
            }

            return result;
        }

        public T Min()
        {
            T result = this.coreData[0];

            for (int i = 1; i < this.Indexer; i++)
            {
                if (this.coreData[i].CompareTo(result) < 0)
                {
                    result = this.coreData[i];
                }
            }

            return result;
        }

        private bool OverflowCheck()
        {
            return this.Indexer >= this.coreData.Length;
        }

        private void Resize()
        {
            T[] tempy = new T[this.coreData.Length * 2];

            for (int i = 0; i < this.coreData.Length; i++)
            {
                tempy[i] = this.coreData[i];
            }

            this.coreData = tempy;
        }

        // close to crap
        //private void Resize(T element, int index)
        //{
        //    T[] tempy = new T[this.coreData.Length * 2];

        //    for (int i = 0; i < index; i++)
        //    {
        //        tempy[i] = this.coreData[i];
        //    }

        //    tempy[index] = element;

        //    for (int i = index + 1; i < this.coreData.Length + 1; i++)
        //    {
        //        tempy[i] = this.coreData[i - 1];
        //    }

        //    this.coreData = tempy;
        //}
    }
}